﻿// 拦截全局http请求，附加或截获返回的token
// 并通过 内置GlobalTokenRefresh 组件实现Token自动刷新

using Blazored.LocalStorage;
using Blazored.LocalStorage.StorageOptions;
using BootstrapBlazor.Components;
using BootstrapBlazorApp.Shared.Data.SYS;
using BootstrapBlazorApp.Shared.Pages.Account;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Rendering;
using OfficeOpenXml.FormulaParsing.ExpressionGraph.FunctionCompilers;
using System.Net.Http.Headers;
using WebApiClientCore;

namespace  BootstrapBlazorApp.Shared.Shared;

/// <summary>
/// Token监听组件
/// </summary>
public sealed class GlobalTokenProvider : ComponentBase, IAsyncDisposable
{
    [Inject] ITokenInterceptor? Interceptor { get; set; }
    [Inject] NavigationManager? Navigation { get; set; }
    [Inject] AuthenticationStateProvider? StateProvider { get; set; }
    [Inject] MessageService? messageService { get; set; }
    [Inject] ILocalStorageService? localStorageService { get; set; }

    /// <summary>
    /// 获得/设置 子组件内容
    /// </summary>
    [Parameter]
    public RenderFragment<TokenContext>? ChildContent { get; set; }

    /// <summary>
    /// 获得/设置 是否初始化完成
    /// </summary>
    private bool Initialized { get; set; }

    /// <summary>
    /// 获得/设置 Token上下文对象
    /// </summary>
    private TokenContext? Context { get; set; }

    /// <summary>
    /// SignalR连接
    /// </summary>
    //private HubConnection? ChatHub { get; set; }

    /// <summary>
    /// OnInitialized
    /// </summary>
    protected override void OnInitialized()
    {
        if (Navigation is null)
        {
            throw new ArgumentNullException(nameof(Navigation));
        }

        if (Interceptor is null)
        {
            throw new ArgumentNullException(nameof(Interceptor));
        }

        if (StateProvider is null)
        {
            throw new ArgumentNullException(nameof(StateProvider));
        }

        Context = new(Interceptor);
        Context.Interceptor.BeforeSend += OnBeforeSend;
        Context.Interceptor.AfterSend += OnAfterSend;
        Context.UpdateClientToken += OnUpdateClientToken;
    }

    /// <summary>
    /// BuildRenderTree 方法
    /// </summary>
    /// <param name="builder"></param>
    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        builder.OpenComponent<GlobalTokenRefresh>(0);
        builder.AddAttribute(1, nameof(GlobalTokenRefresh.Context), Context);
        builder.CloseComponent();
        if (Initialized && ChildContent is not null)
        {
            builder.AddContent(2, ChildContent, Context!);
        }
    }

    /// <summary>
    /// OnAfterRenderAsync
    /// </summary>
    /// <param name="firstRender"></param>
    /// <returns></returns>
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            //第一次渲染只是建立连接，读取客户端LocalStorage
            Context!.LoadToken(await StateProvider!.GetAuthenticationStateAsync());
            if(Context.IsAuthenticated)
            {
                var currentUser = await localStorageService.GetItemAsync<CurrentUser>(ClientAppKey.CurrentUser);
                //设置Session
                Context.SetSession(ClientAppKey.CurrentUser, currentUser);
            }

            //通过该参数强制刷新后重新渲染
            Initialized = true;

            StateHasChanged();
        }
        else
        {
            //await HubConnectAsync();
        }
    }

    /// <summary>
    /// 建立SignalR连接
    /// </summary>
    private async Task HubConnectAsync()
    {
        await Task.CompletedTask;
        //if (ChatHub == null || ChatHub.State == HubConnectionState.Disconnected)
        //{
        //    if (Context!.IsAuthenticated)
        //    {
        //        //admin.net这里使用url传access_token
        //        var query = $"?access_token={Context.AccessToken}";
        //        var builder = new UriBuilder(Navigation!.ToAbsoluteUri("/hubs/chathub")) { Query = query };
        //        ChatHub = new HubConnectionBuilder()
        //            .WithUrl(builder.Uri)
        //            .Build();

        //        //接收消息
        //        ChatHub.On<object>("ReceiveMessage", (message) => { });

        //        //强制退出
        //        ChatHub.On("ForceExist", () => Context?.UpdateToken(false, null, null));

        //        await ChatHub.StartAsync();
        //    }
        //}
    }

    /// <summary>
    /// 请求执行之前
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="context"></param>
    private void OnBeforeSend(object? sender, ApiRequestContext? context)
    {
        if (context == null)
        {
            return;
        }

        var authorization = context.HttpContext.RequestMessage.Headers.Authorization;
        if (authorization != null && authorization.Scheme == "Bearer")
        {
            return;
        }

        if (string.IsNullOrWhiteSpace(Context?.AccessToken))
        {
            return;
        }

        context.HttpContext.RequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Context.AccessToken);
        if (string.IsNullOrWhiteSpace(Context?.RefreshToken))
        {
            return;
        }

        if (Context.IsExpiringSoon)
        {
            context.HttpContext.RequestMessage.Headers.TryAddWithoutValidation("X-Authorization", $"Bearer {Context.RefreshToken}");
        }
    }

    /// <summary>
    /// 请求执行后
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    private void OnAfterSend(object? sender, ApiResponseContext? context)
    {
        //每次请求后需要检查一下响应头部
        //看是否生成了新的token
        if (context == null)
        {
            return;
        }

        //此处需要对接口返回的异常code和状态码进行处理，并进行对应提示，后续补充完善
        //Toast

        if (context.HttpContext.ResponseMessage == null)
        {
            messageService.Show(new MessageOption() {  Content = "无法连接服务器，查询失败!" });
            Navigation.NavigateTo("/");
            return;
        }


        if (context.HttpContext.ResponseMessage.Headers.Contains("access-token") != true)
        {
            return;
        }

        var accessToken = context.HttpContext.ResponseMessage.Headers.GetValues("access-token").FirstOrDefault();
        if (accessToken == "invalid_token")
        {
            //异常Token，注销后重新登录
            Context?.UpdateToken(false, null, null);
        }

        if (context.HttpContext.ResponseMessage.Headers.Contains("x-access-token") != true)
        {
            return;
        }

        var refreshToken = context.HttpContext.ResponseMessage.Headers.GetValues("x-access-token").FirstOrDefault();
        if (string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(refreshToken))
        {
            return;
        }

        //获取到新的Token
        Context?.UpdateToken(true, accessToken, refreshToken);
    }

    /// <summary>
    /// 更新或清除客户端Token信息
    /// <para>如果token认证有效会更新客户端LocalStorage里存储的token</para>
    /// <para>如果无效（包括过时）会清除客户端LocalStorage里存储的token</para>
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    /// <returns>void</returns>
    private async void OnUpdateClientToken(object? sender, (string?, string?) args)
    {
        await ((JwtAuthenticationStateProvider)StateProvider!).UpdateAuthenticated(args.Item1, args.Item2);
    }

    /// <summary>
    /// DisposeAsync
    /// </summary>
    /// <returns></returns>
    public async ValueTask DisposeAsync()
    {
        if (Context != null)
        {
            Context.Interceptor.BeforeSend -= OnBeforeSend;
            Context.Interceptor.AfterSend -= OnAfterSend;
            Context.UpdateClientToken -= OnUpdateClientToken;
        }

        //if (ChatHub != null)
        //{
        //    await ChatHub.DisposeAsync();
        //}
        GC.SuppressFinalize(this);
    }
}
